/*
 * -------------------------------------- 
 * FileName: 1_1.employee_manager.aio.c 
 * Content:
 *      单文件1_1任务 
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */


#ifdef DEBUG_MODE

    #ifdef _WIN32

        #include<windows.h>
        #define _debug(_msg,_type)\
            do{\
                MessageBeep(0);\
                MessageBox(NULL,_msg,_type,0); \
            }while(0)
        
    #endif

    #ifdef __linux__

        #define _debug(_msg,_type)\
            do{
                fprintf(stderr,"[%s]:%s",_msg,_type)\
            }while(0)
    
    #endif

#else

    #define _debug(_msg,_type)\
    do {} while (0)

#endif



#include<malloc.h>
#include<memory.h>
#include<stdio.h>
#include<string.h>




/**
 * =====================
 *  顺序表类型、函数声明
 * =====================
 */

//命名空间宏
#define ns(title) sqList_##title

//顺序表错误
typedef enum ns(error_t)
{
    ns(OK),
    ns(INSUFFICIENT_MEMORORY),
    ns(INDEX_OUT_OF_BOUND),

} ns(error_t);

//顺序表
typedef struct 
{
    void * data;
    //单个元素大小
    size_t _elemSize;
    //元素长度
    size_t _len;
    //不重新分配内存，能容纳的元素数量
    size_t _capacity;
    //错误处理器
    ns(error_t) *_error_handler;

} ns(t);


void ns(init)  (ns(t) * self, size_t elemSize, ns(error_t) * error_handler);
void *ns(get) (ns(t) * self, size_t index);
void ns(reserve) (ns(t) * self, size_t new_len);
void *ns(insertive_get) (ns(t) * self, size_t index);
void ns(uninit) ( ns(t) * self);
void ns(erase) (ns(t) * self, size_t index);

#undef ns



/**
 * =====================
 *  主程序
 * =====================
 */

typedef struct employee_t
{

    // 唯一员工id
    char id[20]; 
    // 员工姓名
    char name[256];
    // 员工职务
    char position[512];
 
} employee_t;


// sqList异常处理，由于两个表都是同种数据结构，就用了一个变量
sqList_error_t app_erros;

//已辞职的员工
sqList_t archivedEmployees;

//现有员工
sqList_t employees;

//欢迎
const char motto[] = 
"====================================================================== \n"\
"      Task 1 :      Employee Manage System  (sequential list):         \n"\
"---------------------------------------------------------------------- \n"\
"    welcome! You can use \"help\" command to get helper docs           \n"\
"                                                                       \n"\
"                                                                       \n"\
"====================================================================== \n";

//帮助文档
const char helping[] = 
"======================================================================\n"
"add : \"add <id> <name> [discription]\"\n"
"   add a employee to currentEmployee list.\n"
"del : \"del <id> [-f(permanently)]\"\n"
"   del a employee from list, move it to Disinged employ list\n"
"       if \'-f\' is set, delete the entry permanently.\n"
"list : \"list [-a(show disigned)]\".\n"
"   list employee list.\n"
"show : \"show <id>\"\n"
"   print the name and discription of the entry with same id\n"
"quit : \"quit\".\n"
"   quit the programme\n"
"help : \"help\".\n"
"   print this page.\n"
"======================================================================\n";

static void cmd_list(const char *args);
static void cmd_quit(const char *);
static void cmd_add(const char * args);
static void cmd_query(const char * args);
static void cmd_help(const char * args);
static void cmd_delete(const char * args);

void init()
{
    printf("initing...\n");

    // 初始化数据结构
    app_erros = sqList_OK;
    sqList_init(&archivedEmployees, sizeof(employee_t), &app_erros);
    sqList_init(&employees, sizeof(employee_t), &app_erros);

    if(app_erros != sqList_OK)
    {
        printf("Encounter errors in initing database.\n");
        return;
    }

    printf(motto);
}




int running = 0;

int main(int argc, char * argv[])
{
    char cmd_buffer[1024];


    init();

    struct cmd_t {
        char command_name[20];
        void (*command)  (const char *);
    };

    struct cmd_t cmds[] = {
       {"list", cmd_list},
       {"add",  cmd_add},
       {"quit", cmd_quit},
       {"del", cmd_delete},
       {"show", cmd_query},
       {"help", cmd_help}
    };

    running = 1;
    while(running)
    {
        printf("\r>");
        fflush(stdout);

        //获取用户一行的输入
        fgets(cmd_buffer,1000,stdin);

        char ins[20];
        int offset;

        // 从用户输入的内容里找命令
        int all_len = strlen(cmd_buffer);
        sscanf(cmd_buffer,"%19s%n",ins,&offset);

        // 遍历所有命令，找到对应的，就执行
        unsigned char matched = 0;
        for(int i = 0; i < sizeof(cmds) / sizeof(struct cmd_t); ++ i)
        {
            if(0 == strcmp(cmds[i].command_name,ins) && all_len > offset)
            {
                cmds[i].command(cmd_buffer+offset);
                matched = 1;
            }
        }
        if(matched == 0)
        {
            printf("unrecongized command: %19s\n",ins);
        }
    }
    return 0;
}





static void cmd_list(const char *args)
{
    // 是否显示全部的 flag.
    unsigned char show_all_flag = 0;

    // 捕获参数，判断是否含有 'a'.
    char param[20];
    sscanf(args, "%19s", param);

    int n = strlen(param);
    for(int i = 0; i < n; ++ i)
    {
        if(param[i] == 'a')
        {
            show_all_flag = 1;
        }
    }

    // 列举当前员工列表
    printf("List of current employees, %u in total:\n", employees._len);
    for(int i = 0; i < employees._len; ++ i)
    {
        employee_t *p = ((employee_t *)sqList_get(&employees,i));
        printf("id:%s name:%s\n", p->id, p->name);
    }

    if(show_all_flag)
    {
        // 列举已辞职员工列表
        printf("List of disigned employees, %u in total:\n",  archivedEmployees._len);
        for(int i = 0; i < archivedEmployees._len; ++ i)
        {
            employee_t *p = ((employee_t *)sqList_get(&archivedEmployees,i));
            printf("id:%s name:%s\n", p->id, p->name);
        }
    }
}

static void cmd_quit(const char * _args)
{
    sqList_uninit(&archivedEmployees);
    sqList_uninit(&employees);

    running = 0;
}

static void cmd_add(const char * args)
{
    employee_t tmp;
    *tmp.position = '\0';
    
    int nums = sscanf(args,"%19s%255s%511s", &tmp.id, &tmp.name, &tmp.position);
    
    //参数不合法
    if(nums < 2)
    {
        printf("usage : \"add <id> <name> [discription]\".\n");
        return;
    }

    // 遍历员工
    for(int i = 0; i < employees._len; ++ i)
    {
        employee_t* itr = (employee_t*)sqList_get(&employees, i);
        if(0==strcmp(tmp.id,(itr ->id)))
        {
            printf("duplicate id (same as %s's). not added.\n", itr->name);
            return;
        }
    }

    // 遍历已经辞职的员工。注：员工虽然辞职了但是id不能冲突，除非强制删除。
    for(int i = 0; i < archivedEmployees._len; ++ i)
    {
        employee_t* itr = (employee_t*)sqList_get(&archivedEmployees, i);
        if(0==strcmp(tmp.id,(itr ->id)))
        {
            printf("duplicate id (same as %s(resigned)'s). not added.\n", itr->name);
            return;
        }
    }

    size_t idx = employees._len;
    employee_t* empl = (employee_t*) sqList_insertive_get(&employees, idx);
    memcpy(empl, &tmp, sizeof(employee_t));
}

static void cmd_delete(const char * args)
{
    unsigned char force_delete_flag = 0;

    char id[21];
    char param[21];

    int num = sscanf(args, "%20s%20s", id, param);

    //参数不合法
    if(num < 1)
    {
        printf("usage : \"del <id> [-f(fore delete)]\"\n");
        return;
    }

    if(num == 2)
    {
        int len = strlen(param);
        for(int i = 0; i < len; ++ i)
        {
            if(param[i] == 'f')
            {
                force_delete_flag = 1;
            }
        }
    }

    //遍历所有员工
    size_t idx = employees._len;
    for(int i = 0; i < idx; ++ i)
    {
        employee_t* itr = (employee_t*)sqList_get(&employees, i);
     
        // 匹配员工 id
        if(0==strcmp(id,(itr ->id)))
        {
            // 如果没有设置 -f 就把这个插入到辞职员工表里面。
            if(force_delete_flag == 0)
            {
                employee_t* newplace = (employee_t*)sqList_insertive_get(&archivedEmployees, archivedEmployees._len);
                if(newplace == NULL)
                {
                    printf("fatal: fail to move");
                    return;
                }
                memcpy(newplace,sqList_get(&employees, i),sizeof(employee_t));
            }

            sqList_erase(&employees,i);
            return;
        }
    }

    // 如果 -f 加上了，就会去从辞职员工列表里查找、删除。
    if(force_delete_flag)
    {
        idx = archivedEmployees._len;
        for(int i = 0; i < idx; ++ i)
        {
            employee_t* itr = (employee_t*)sqList_get(&archivedEmployees, i);
            if(0==strcmp(id,(itr ->id)))
            {
                sqList_erase(&archivedEmployees,i);
                return;
            }
        }
    }
    

    printf("not found.\n");

}

static void cmd_query(const char * args)
{
    char id[20];
    char param[20];

    int num = sscanf(args, "%20s%20s", id, param);

    // 参数非法
    if(num < 1)
    {
        printf("usage : \"show <id>\"\n");
        return;
    }
    

    //遍历所有员工
    size_t idx = employees._len;
    for(int i = 0; i < idx; ++ i)
    {
        employee_t* itr = (employee_t*)sqList_get(&employees, i);
        if(0==strcmp(id,(itr ->id)))
        {
            printf("name: %s\ndiscription: %s\n", itr->name, itr->position);
            return;
        }
    }

    idx = archivedEmployees._len;
    for(int i = 0; i < idx; ++ i)
    {
        employee_t* itr = (employee_t*)sqList_get(&archivedEmployees, i);
        if(0==strcmp(id,(itr ->id)))
        {
            printf("name: %s(disigned)\ndiscription: %s\n", itr->name, itr->position);
            return;
        }
    }

    printf("Not found.\n");
}

static void cmd_help(const char * _args)
{
    printf(helping);
    return;
}



/**
 * =====================
 *  顺序表函数实现
 * =====================
 */

#define ns(title) sqList_##title

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)


// 默认顺序表长度
const int sqList_default_length = 80;
// 默认顺序表增长量
const int sqList_dft_incri = 80;


//初始化顺序表
void sqList_init( sqList_t * self, size_t elemSize, sqList_error_t * error_handler)
{
    self->_error_handler = error_handler;
    self->_elemSize = elemSize;
    self->_len = 0;
    self->_capacity = sqList_default_length;

    self->data = malloc(self->_elemSize * self->_capacity);

    if(self->data == NULL) 
    {
        self->_capacity = 0;

        try_error(sqList_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    return;
}

//析构顺序表
void sqList_uninit( sqList_t * self)
{
    self->_elemSize = 0;
    self->_len = 0;
    self->_capacity = 0;

    free(self->data);

    return;
}

// 获取位于index偏移量的元素地址
void * sqList_get(sqList_t * self, size_t index)
{
    if(index >= self->_len)
    {
        try_error(sqList_INDEX_OUT_OF_BOUND);

        _debug("Index Too much","Error");
        
        return NULL;
    }

    return (unsigned char*)self->data + index * (self->_elemSize);
}

// 拓容与缩容
void sqList_reserve(sqList_t * self, size_t new_len)
{

    if(new_len <= sqList_default_length)
    {
        return;
    }

    void * tmp = realloc(self->data,new_len * self->_elemSize);

    if(tmp == NULL) 
    {

        try_error(sqList_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    self->data = tmp;
    self->_capacity = new_len;
}

// 在指定位置插入空元素，并返回
void * sqList_insertive_get(sqList_t * self, size_t index)
{
    if(index > self->_len)
    {
        try_error(sqList_INDEX_OUT_OF_BOUND);

        _debug("Index Too much","Error");
        
        return NULL;
    }
    
    if(self->_len + 1 > self->_capacity) 
    {
        size_t tmp = self->_capacity;
        sqList_reserve(self, self->_capacity + sqList_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(sqList_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");

            return NULL;
        }
    }

    // 移动所有位于后方的元素
    for(size_t i = self->_len + 1; i + 1 > index; --i)
    {
        memcpy(
            (unsigned char*)self->data + (i) * self->_elemSize,
            (unsigned char*)self->data + (i-1) * self->_elemSize,
            self->_elemSize
        );
    }

    self->_len += 1;

    // 返回空出来的位
    return (unsigned char *)self->data + index * self->_elemSize;
}

void sqList_erase(sqList_t * self, size_t index)
{
    // 超出下标访问处理，包括从空表中删除
    if(index >= self->_len)
    {
        try_error(sqList_INDEX_OUT_OF_BOUND);

        _debug("Index Too much","Error");
        
        return;
    }
    
   
    //移动所有后方元素
    for(size_t i = index; i < self->_len; ++i)
    {
        memcpy(
            (unsigned char *)self->data + (i) * self->_elemSize,
            (unsigned char *)self->data + (i+1) * self->_elemSize,
            self->_elemSize
        );
    }

    // 判断是否达到需要缩容的情况
    if((self->_len - 1)/sqList_dft_incri != (self->_len - 1)/sqList_dft_incri) 
    {
        size_t tmp = self->_capacity;
        sqList_reserve(self, self->_capacity - sqList_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(sqList_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");
            
            return;
        }
    }

    self->_len -= 1;

    return;
}

#undef try_error
#undef ns
